package cn.qingyun.gis.modules.scenes.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.qingyun.gis.exception.CommonException;
import cn.qingyun.gis.modules.express.entity.ExpressRegion;
import cn.qingyun.gis.modules.express.service.IExpressRegionService;
import cn.qingyun.gis.modules.express.util.SyncTool;
import cn.qingyun.gis.modules.scenes.contant.ComConstant;
import cn.qingyun.gis.modules.scenes.contant.ScenesStatus;
import cn.qingyun.gis.modules.scenes.entity.PoiChannelModel;
import cn.qingyun.gis.modules.scenes.entity.PoiNumberInfo;
import cn.qingyun.gis.modules.scenes.entity.PoiScenarioChannel;
import cn.qingyun.gis.modules.scenes.mapper.PoiNumberInfoMapper;
import cn.qingyun.gis.modules.scenes.request.PoiNumberInfoRequest;
import cn.qingyun.gis.modules.scenes.response.PoiNumberInfoResponse;
import cn.qingyun.gis.modules.scenes.service.IPoiNumberInfoService;
import cn.qingyun.gis.modules.scenes.service.IPoiScenarioChannelService;
import cn.qingyun.gis.modules.scenes.service.IPoiUserPermissionService;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author guest
 * @since 2024-06-13
 */
@Service
@Slf4j
@DS("scenes_offensive")
public class PoiNumberInfoServiceImpl extends ServiceImpl<PoiNumberInfoMapper, PoiNumberInfo> implements IPoiNumberInfoService {
    @Resource
    private IExpressRegionService iExpressRegionService;
    @Resource
    private IPoiScenarioChannelService iPoiScenarioChannelService;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private IPoiUserPermissionService IPoiUserPermissionService;

    @Override
    public IPage<PoiNumberInfoResponse> selectPeopleDetailPage(PoiNumberInfoRequest poiNumberInfoRequest) {
        log.debug("责任人列表查询入参，{}", poiNumberInfoRequest);
        LambdaQueryWrapper<ExpressRegion> expressRegionWrapper = new LambdaQueryWrapper<>();
        expressRegionWrapper.select(ExpressRegion::getLevel).eq(ExpressRegion::getCode, poiNumberInfoRequest.getCode());
        Integer level = Integer.valueOf(iExpressRegionService.getOne(expressRegionWrapper).getLevel());
        int totalResult=0;
        long timeStart = System.currentTimeMillis();
        if (2 == level && StringUtils.isEmpty(poiNumberInfoRequest.getType()) && StringUtils.isEmpty(poiNumberInfoRequest.getChannelCode()) &&
                StringUtils.isEmpty(poiNumberInfoRequest.getKwPhone()) && StringUtils.isEmpty(poiNumberInfoRequest.getPoiId())) {
            Integer count = (Integer) redisTemplate.opsForValue().get("poiNumber:info:count:" + poiNumberInfoRequest.getCode());
            if (count != null) {
                totalResult=count;
            } else {
                totalResult = iPoiScenarioChannelService.selectPoiNumberInfoCountSub(poiNumberInfoRequest, ComConstant.TABLES.get(0), level);
                if (totalResult > 0) {
                    redisTemplate.opsForValue().setIfAbsent("poiNumber:info:count:" + poiNumberInfoRequest.getCode(), totalResult, 60, TimeUnit.SECONDS);
                }
            }
        } else {
                totalResult = iPoiScenarioChannelService.selectPoiNumberInfoCountSub(poiNumberInfoRequest, ComConstant.TABLES.get(0), level);
        }
        log.debug("统计的总个数，{}", totalResult);
        long timeWaste = System.currentTimeMillis();
        log.debug("统计总数耗时==" + (timeWaste - timeStart));
        int startIndex = (poiNumberInfoRequest.getPageNum() - 1) * poiNumberInfoRequest.getPageSize();

        poiNumberInfoRequest.setPageNum(startIndex);
        List<PoiNumberInfoResponse> recordList = iPoiScenarioChannelService.selectPoiNumberInfoPage(poiNumberInfoRequest, ComConstant.TABLES, level);
        log.debug("分页耗时==" + (System.currentTimeMillis() - timeWaste));
        recordList.forEach(entity -> {
            //处理商业综合体 变为 商务楼宇
            if (ScenesStatus.COMPLEX.getDesc().equals(entity.getType())) {
                entity.setType(ScenesStatus.BUSINESS.getDesc());
            }
        });
        IPage<PoiNumberInfoResponse> res = new Page<>();
        res.setTotal(totalResult);
        res.setRecords(recordList);
        res.setCurrent(poiNumberInfoRequest.getPageNum());
        res.setSize(poiNumberInfoRequest.getPageSize());
        return res;
    }

    @SneakyThrows
    @Override
    public void peopleDetailExport(PoiNumberInfoRequest poiNumberInfoRequest, HttpServletResponse response) {
        log.debug("责任人导出传入参，{}", poiNumberInfoRequest);
        //判断当前人是否有权限导出
        boolean flag = IPoiUserPermissionService.hasPermission();
        log.debug("查询当前人是否有权限导出返回得结果数据，{}", flag);
        if (!flag) {
            throw new CommonException("当前工号没有权限操作");
        }
        String fileName = "责任人明细";
        response.setContentType("application/vnd.ms-excel");
        response.setHeader("content-disposition", String.format("attachment;filename=%s.xlsx", new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + DateUtil.now()));
        LambdaQueryWrapper<ExpressRegion> expressRegionWrapper = new LambdaQueryWrapper<>();
        expressRegionWrapper.select(ExpressRegion::getLevel).eq(ExpressRegion::getCode, poiNumberInfoRequest.getCode());
        int level = Integer.parseInt(iExpressRegionService.getOne(expressRegionWrapper).getLevel());
        if (2 == level) {
            LambdaQueryWrapper<ExpressRegion> regionQueryWrapper = new LambdaQueryWrapper<>();
            regionQueryWrapper.eq(ExpressRegion::getParentCode, poiNumberInfoRequest.getCode());
            List<ExpressRegion> expressRegions = iExpressRegionService.list(regionQueryWrapper);
            List<String> disCodes = expressRegions.stream().map(ExpressRegion::getCode).collect(Collectors.toList());
            level = 3;
            try (ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream()).build()) {
                WriteSheet writeSheet = EasyExcelFactory.writerSheet(0, fileName).head(PoiNumberInfoResponse.class).build();
                Integer finalLevel = level;
                SyncTool.doSync(disCodes.size(), i -> {
                    PoiNumberInfoRequest request = new PoiNumberInfoRequest();
                    BeanUtils.copyProperties(poiNumberInfoRequest, request);
                    request.setCode(disCodes.get(i - 1));
                    List<PoiNumberInfoResponse> recordList = iPoiScenarioChannelService.selectPoiNumberInfoPageSub(request, ComConstant.TABLES, finalLevel);
                    recordList.forEach(entity -> {
                        //处理商业综合体 变为 商务楼宇
                        if (ScenesStatus.COMPLEX.getDesc().equals(entity.getType())) {
                            entity.setType(ScenesStatus.BUSINESS.getDesc());
                        }
                    });
                    synchronized (excelWriter) {
                        excelWriter.write(recordList, writeSheet);
                    }
                });
            }
        } else {
            int finalLevelSub = level;
            int count = iPoiScenarioChannelService.selectPoiNumberInfoCountSub(poiNumberInfoRequest, ComConstant.TABLES.get(0), finalLevelSub);
            int batchSize = 5000;
            int pages = count / batchSize + 1;
            try (ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream()).build()) {
                WriteSheet writeSheet = EasyExcelFactory.writerSheet(0, fileName).head(PoiNumberInfoResponse.class).build();
                SyncTool.doSync(pages, i -> {
                    int startIndex = i == 1 ? 0 : (i - 1) * batchSize;
                    PoiNumberInfoRequest request = new PoiNumberInfoRequest();
                    BeanUtils.copyProperties(poiNumberInfoRequest, request);
                    request.setPageSize(batchSize);
                    request.setPageNum(startIndex);
                    List<PoiNumberInfoResponse> recordList = iPoiScenarioChannelService.selectPoiNumberInfoPage(request, ComConstant.TABLES, finalLevelSub);
                    recordList.forEach(entity -> {
                        //处理商业综合体 变为 商务楼宇
                        if (ScenesStatus.COMPLEX.getDesc().equals(entity.getType())) {
                            entity.setType(ScenesStatus.BUSINESS.getDesc());
                        }
                    });
                    synchronized (excelWriter) {
                        excelWriter.write(recordList, writeSheet);
                    }
                });
            }
        }
    }

    @Async
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void processFile(InputStream inputStream) {
        CopyOnWriteArrayList<PoiScenarioChannel> poiStreetList = new CopyOnWriteArrayList<>();
        int batchSize = 3000;
        EasyExcel.read(inputStream, PoiChannelModel.class, new AnalysisEventListener<PoiChannelModel>() {
            @Override
            public void invoke(PoiChannelModel data, AnalysisContext context) {
                PoiScenarioChannel poiScenarioChannel = new PoiScenarioChannel();
                BeanUtils.copyProperties(data, poiScenarioChannel);
                poiStreetList.add(poiScenarioChannel);
                if (poiStreetList.size() >= batchSize) {
                    iPoiScenarioChannelService.insertOrUpdateByPoiId(poiStreetList, "poi_scenario_channel");
                    poiStreetList.clear();
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                if (CollectionUtils.isNotEmpty(poiStreetList)) {
                    iPoiScenarioChannelService.insertOrUpdateByPoiId(poiStreetList, "poi_scenario_channel");
                    poiStreetList.clear();
                }
            }
        }).sheet().headRowNumber(2).doRead();
    }

}
